package uk.ac.cam.echo.reader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Enumeration;

import gnu.io.*;

/**
 * @author Andrius Dagys
 */

public class Communicator {
	//
	// Declare constants
	//
	
	private SerialPort port = null;
	BufferedReader is = null;
	PrintStream os = null;
	private char EOP;

	/**
	 * Initialize the object to interface serial communications
	 * 
	 * @param portName name of the port to use
	 */
	public Communicator(String portName, int baudRate) {
		//
		// Set up the port
		//
		openSerialPort(portName, baudRate);
		//
		// Open the input Reader and input stream.
		//
		try {
			is = new BufferedReader(
					new InputStreamReader(port.getInputStream()));
		} catch (IOException e) {
			System.err.println("Can't open input stream: write-only");
			is = null;
		}
		//
		// Open the output stream
		//
		try {
			os = new PrintStream(port.getOutputStream(), true, "ISO-8859-1");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Establish a connection to the port
	 * 
	 * @param portName Name of the port to open
	 */
	private void openSerialPort(String portName, int baudRate) {
		//
		// Get an enumeration of all ports known to JavaComm
		//
		Enumeration portIdentifiers = CommPortIdentifier.getPortIdentifiers();
		//
		// Check each port identifier if
		// (a) it indicates a serial (not a parallel) port, and
		// (b) matches the desired name.
		//
		CommPortIdentifier portId = null; // will be set if port found
		while (portIdentifiers.hasMoreElements()) {
			CommPortIdentifier pid = (CommPortIdentifier) portIdentifiers
					.nextElement();
			if (pid.getPortType() == CommPortIdentifier.PORT_SERIAL
					&& pid.getName().equals(portName)) {
				portId = pid;
				break;
			}
		}
		if (portId == null) {
			System.err.println("Could not find serial port " + portName);
			System.exit(1);
		}
		//
		// Use port identifier for acquiring the port
		//
		try {
			port = (SerialPort) portId.open("name", 1000);

		} catch (PortInUseException e) {
			System.err.println("Port already in use: " + e);
			System.exit(1);
		}
		//		
		// Set all the serial port parameters
		//
		try {
			port.setSerialPortParams(baudRate, SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

		} catch (UnsupportedCommOperationException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Set the end of packet character
	 * @param eop The character specifying the end of the packet
	 */
	public void setPacketFormat(char eop){
		this.EOP = eop;		
	}
	/**
	 * Transmit a command to the serial device.
	 * @param cmd
	 */
	public void send(String cmd) {
		os.print(cmd);
	}
	
	/**
	 * Read the response from the serial device.
	 * Returns the response string.
	 */
	public String get() {
		String response = "";
		try {
			char c;
			do {
				c = (char) is.read();
				response += c;	
			} while (c != EOP);

		} catch (IOException e) {
			e.printStackTrace();
		}
		return response;
	}
	
	/**
	 * Converts the given hex string to a single byte
	 * @param cmd A hex string representing the command
	 */
	public void sendSingleHexByte(String cmd) {
		Byte b = Byte.parseByte(cmd,16);
		os.write(b);
		os.flush();
	}

	/**
	 * Release Java, driver and OS resources
	 */
	public void closePort() {
		try {
			if (is != null)
				is.close();
			if (os != null)
				os.close();
			if (port != null)
				port.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
